package org.oschina.api.blog.blog.dao;

import java.util.Iterator;
import java.util.List;
import org.oschina.Constants;
import org.oschina.api.BaseDAO;
import org.oschina.api.blog.blog.model.Blog;
import org.oschina.api.blog.blog.vo.BlogVo;
import org.oschina.api.blog.catalog.dao.BlogCatalogDAO;
import org.oschina.api.comment.dao.CommentDAO;
import org.oschina.api.index.model.IndexHandleBean;
import org.oschina.api.user.user.dao.UserDAO;
import org.oschina.helper.cache.ICachePipeline;
import org.oschina.helper.index.IndexHelper;
import org.oschina.helper.index.IndexResultBean;
import org.oschina.helper.query.IQueryCacheCallback;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

@Repository
public class BlogDAO extends BaseDAO {

	@Autowired
	private IndexHelper indexHelper;
	@Autowired
	private CommentDAO commentDAO;
	@Autowired
	private UserDAO userDAO;
	@Autowired
	private BlogCatalogDAO catalogDAO;
	
	private final String findVo="SELECT bg.id,bg.title,bg.content,us.id as 'author.id',us.username as 'author.username',bg.comment_id,bg.type,gbc.id as 'catalog.id',gbc.name as 'catalog.name'" +
			",bgo.id as 'order.id',bgo.name as 'order.name',bg.comment,bg.oneself,bg.views_count,bg.comment_count,bg.favorite_count,bg.recommend_time,bg.create_time" +
			" FROM "+Constants.DB_BLOG+" bg,"+Constants.DB_USER+" us,"+Constants.DB_BLOG_CATALOG+" gbc,"+Constants.DB_BLOG_ORDER+" bgo" +
			" WHERE us.id=bg.author_id AND bg.catalog=gbc.id AND bg.`order`=bgo.id";
	
	private final String findVoByTypeAndRecommend=findVo+" AND bg.type=? AND bg.recommend_time!=0 ORDER BY bg.recommend_time DESC LIMIT ?,?";
	/**
	 * 根据类型和推荐返回
	 * @param type
	 * @param first
	 * @param max
	 * @return
	 */
	public List<BlogVo> findVoByTypeAndRecommend(int type,int first,int max)
	{
		List<BlogVo> result=null;
		if(first==0)
			result=getQueryHelper().query_cache(findVoByTypeAndRecommend, new Object[]{type,first,max}, BlogVo.class);
		else
			result=getQueryHelper().query(findVoByTypeAndRecommend, new Object[]{type,first,max}, BlogVo.class);
		
		Iterator<BlogVo> iterator=result.iterator();
		while(iterator.hasNext())
		{
			BlogVo blogVo=iterator.next();
			
			if(!blogVo.getType().equals(type) || blogVo.getRecommend_time()==0)
			{
				iterator.remove();
				continue;
			}
			
			if(blogVo.getOneself())
				blogVo.setContent(null);
		}
		
		return result;
	}
	
	/**
	 * 返回推荐数量
	 * @param type
	 * @return
	 */
	public int getCountByTypeAndRecommend(int type)
	{
		return (int) getQueryHelper().queryDataSum_cache(new String[]{"type=?","recommend_time!=0"}, new Object[]{type},BlogVo.class);
	}
	
	private final String findVoByTypeAndAuthor=findVo+" AND bg.author_id=? AND bg.type=? ORDER BY case when bgo.`index` is null then 1 else 0 end, bgo.`index`,bg.create_time DESC LIMIT ?,?";
	/**
	 * 根据类型和作者返回
	 * @param type
	 * @param author
	 * @param first
	 * @param max
	 * @return
	 */
	public List<BlogVo> findVoByTypeAndAuthor(int type,int author,int first,int max)
	{
		List<BlogVo> result=null;
		if(first==0 && isNeedCache(author))
			result=getQueryHelper().query_cache(findVoByTypeAndAuthor, new Object[]{author,type,first,max}, BlogVo.class);
		else
			result=getQueryHelper().query(findVoByTypeAndAuthor, new Object[]{author,type,first,max}, BlogVo.class);
		
		Iterator<BlogVo> iterator=result.iterator();
		while(iterator.hasNext())
		{
			BlogVo blogVo=iterator.next();
			
			if(!blogVo.getType().equals(type))
			{
				iterator.remove();
				continue;
			}
			
			if(blogVo.getOneself())
				blogVo.setContent(null);
		}
		return result;
	}

	/**
	 * 根据类型和作者返回数量
	 * @param type
	 * @return
	 */
	public int getCountByTypeAndAuthor(int type,int author)
	{
		if(isNeedCache(author))
			return (int) getQueryHelper().queryDataSum_cache(new String[]{"type=?","author_id=?"}, new Object[]{type,author},BlogVo.class);
		else
			return (int) getQueryHelper().queryDataSum(new String[]{"type=?","author_id=?"}, new Object[]{type,author},BlogVo.class);
	}
	
	private final String findVoByTypeAndAuthorAndCatalog=findVo+" AND bg.author_id=? AND bg.catalog=? AND bg.type=? ORDER BY case when bgo.`index` is null then 1 else 0 end, bgo.`index`,bg.create_time DESC LIMIT ?,?";
	/**
	 * 根据类型和作者还有目录返回
	 * @param type
	 * @param author
	 * @param catalog
	 * @param first
	 * @param max
	 * @return
	 */
	public List<BlogVo> findVoByTypeAndAuthorAndCatalog(final int type,final int author,final int catalog,int first,int max)
	{
		List<BlogVo> result=null;
		if(isNeedCache(author))
		{
			result=getQueryHelper().query_cache(getByTypeAndAuthorAndCatalogCache(type, author, catalog),getTimeout(), Constants.MAX_CACHE_BLOG, first, max, BlogVo.class,
				new IQueryCacheCallback()
				{
					@Override
					public List get(int first, int max)
					{
						return _findVoByTypeAndAuthorAndCatalog(type,author,catalog,first,max);
					}
				}
			);
		}
		else
			result=_findVoByTypeAndAuthorAndCatalog(type,author,catalog,first,max);
		
		Iterator<BlogVo> iterator=result.iterator();
		while(iterator.hasNext())
		{
			BlogVo blogVo=iterator.next();
			
			if(!blogVo.getType().equals(type) || !blogVo.getCatalog().getId().equals(catalog))
			{
				iterator.remove();
				continue;
			}
			
			if(blogVo.getOneself())
				blogVo.setContent(null);
		}
		return result;
	}
	private List<BlogVo> _findVoByTypeAndAuthorAndCatalog(int type,int author,int catalog,int first,int max)
	{
		return getQueryHelper().query(findVoByTypeAndAuthorAndCatalog, new Object[]{author,catalog,type,first,max}, BlogVo.class);
	}
	private String getByTypeAndAuthorAndCatalogCache(int type,int author,int catalog)
	{
		return getKey(Constants.CACHE_BLOG_BY_TYPE_CATALOG_AUTHOR,author,catalog,type);
	}
	
	/**
	 * 更新目录
	 * @param author
	 * @param type
	 * @param sourceCatalog
	 * @param targetCatalog
	 * @return
	 */
	public int updateCatalogByAuthorAndType(int author,int type,int sourceCatalog,int targetCatalog)
	{
		int result=(int)getQueryHelper().update(new String[]{"catalog=?"}, new String[]{"author_id=?","catalog=?"}, new Object[]{targetCatalog,author,sourceCatalog},BlogVo.class);
		if(isNeedCache(author))
		{
			List<Integer> blogIdCache=getCacheHelper().lget(getByTypeAndAuthorAndCatalogCache(type,author,sourceCatalog),0,-1,Integer.class);
			ICachePipeline pipeline=getCacheHelper().getPipeline();
			for(Integer id:blogIdCache)
			{
				if(id!=-1)
					pipeline.hset(getIdKey(id), "catalog", targetCatalog, getTimeout());
			}
			
			pipeline.del(getByTypeAndAuthorAndCatalogCache(type,author,sourceCatalog));
			pipeline.del(getByTypeAndAuthorAndCatalogCache(type,author,targetCatalog));
			pipeline.sync();
			
			catalogDAO.updateCount(targetCatalog, result);
		}
		return result;
	}
	
	/**
	 * 根据类型和作者还有目录返回数量
	 * @param type
	 * @param catalog
	 * @param author
	 * @return
	 */
	public int getCountByTypeAndAuthorAndCatalog(int type,int catalog,int author)
	{
		if(!isNeedCache(author))
			return _getCountByTypeAndAuthorAndCatalog(type, catalog, author);
		
		String key=getCountByTypeAndAuthorAndCatalogCache(type,catalog,author);
		Integer result=(Integer) getCacheHelper().get(key, Integer.class);
		if(result==null)
		{
			result=_getCountByTypeAndAuthorAndCatalog(type,catalog,author);
			getCacheHelper().set(key, result, getTimeout());
		}
		return result;
	}
	public int _getCountByTypeAndAuthorAndCatalog(int type,int catalog,int author)
	{
		return (int) getQueryHelper().queryDataSum(new String[]{"type=?","author_id=?","catalog=?"}, new Object[]{type,author,catalog},BlogVo.class);
	}
	private String getCountByTypeAndAuthorAndCatalogCache(int type,int catalog,int author)
	{
		return getKey(Constants.CACHE_BLOG_COUNT_BY_TYPE_CATALOG_AUTHOR,author,catalog,type);
	}
	
	private final String findVoById=findVo+" AND bg.id=?";
	/**
	 * 根据ID返回
	 * @param id
	 * @return
	 */
	public BlogVo findVoById(int id)
	{
		BlogVo cache=_findVoByCache(id);
		
		if(cache!=null)
		{
			cacheData(cache);
			return cache;
		}
		
		BlogVo blogVo=(BlogVo) getQueryHelper().queryForObject(findVoById, new Object[]{id}, BlogVo.class);
		
		if(blogVo==null)
			return null;
		
		if(isNeedCache(blogVo.getAuthor().getId()))
			getCacheHelper().hset(getIdKey(id), blogVo, BlogVo.class,getTimeout());
		
		return blogVo;
	}
	private BlogVo _findVoByCache(int id)
	{
		return (BlogVo) getCacheHelper().hget(getIdKey(id) ,BlogVo.class);
	}
	private boolean _isInCache(int id)
	{
		return getCacheHelper().exists(getIdKey(id));
	}
	
	/**
	 * 根据类型搜索
	 * @param keywords
	 * @param type
	 * @param first
	 * @param max
	 * @return
	 */
	public IndexResultBean<BlogVo> searchVoByType(String keywords[],int type,int first,int max)
	{
		StringBuilder where=null;
		for(String keyword:keywords)
		{
			if(where==null)
				where=new StringBuilder();
			else
				where.append(" OR ");
			
			where.append("(title:'");
			where.append(keyword);
			where.append("' OR content:'");
			where.append(keyword);
			where.append("')");
		}
		where.append(" AND type:");
		where.append(type);
		where.append(" AND oneself:false");
		return indexHelper.query(Constants.INDEX_PATH_BLOG, where.toString(), new String[]{"create_time"}, new String[]{"desc"}, first, max, BlogVo.class);
	}
	
	/**
	 * 返回相关博客
	 * @param blog
	 * @param first
	 * @param max
	 * @return
	 */
	public List<BlogVo> findByRelated(BlogVo blog,int first,int max)
	{
		if(!_isInCache(blog.getId()))
			return _findByRelated(blog,first,max);
		
		String key=this.getKey(Constants.CACHE_BLOG_RELATED, blog.getId());
		List<BlogVo> result=(List<BlogVo>) getCacheHelper().get(key, List.class);
		if(result==null)
		{
			result=_findByRelated(blog,first,max);
			getCacheHelper().set(key, result, Constants.TIMEOUT_BLOG_RELATED);
		}
		return result;
	}
	private List<BlogVo> _findByRelated(BlogVo blog,int first,int max)
	{
		return searchVoByType(new String[]{blog.getTitle()}, blog.getType(), first, max).getResult();
	}
	
	/**
	 * 更新浏览数量
	 * @param id
	 * @param count
	 */
	public void updateViewsCount(int id,int count)
	{
		getCacheHelper().hincr(getIdKey(id), "views_count", count);
		batchUpdate_queue("UPDATE "+Constants.DB_BLOG+" SET views_count=views_count+? WHERE id=?", new Object[]{count,id});
	}
	
	/**
	 * 更新评论数量
	 * @param id
	 * @param count
	 */
	public void updateCommentCount(int id,int count)
	{
		getCacheHelper().hincr(getIdKey(id), "comment_count", count);
		batchUpdate_queue("UPDATE "+Constants.DB_BLOG+" SET comment_count=comment_count+? WHERE id=?", new Object[]{count,id});
	}
	
	/**
	 * 更新关注数量
	 * @param id
	 * @param count
	 */
	public void updateFavoriteCount(int id,int count)
	{
		getCacheHelper().hincr(getIdKey(id), "favorite_count", count);
		batchUpdate_queue("UPDATE "+Constants.DB_BLOG+" SET favorite_count=favorite_count+? WHERE id=?", new Object[]{count,id});
	}
	
	/**
	 * 更新类型
	 * @param id
	 * @param type
	 * @return
	 */
	public int updateType(int id,int type)
	{
		updateIndexById(id,new String[]{"type"}, new Object[]{type});
		updateCache(id,new String[]{"type"},new Object[]{type});
		return (int) getQueryHelper().update(new String[]{"type=?"}, new String[]{"id=?"}, new Object[]{type,id},BlogVo.class);
	}
	
	/**
	 * 编辑
	 * @param blog
	 * @return
	 */
	public int edit(Blog blog)
	{
		updateIndexById(blog.getId(),new String[]{"title","content","oneself"}, new Object[]{blog.getTitle(),blog.getContent(),blog.getOneself()});
		
		updateCache(blog.getId(),
				new String[]{"title","content","order","catalog","comment","oneself"},
				new Object[]{blog.getTitle(),blog.getContent(),blog.getOrder(),blog.getCatalog(),blog.getComment(),blog.getOneself()});
		
		return (int) getQueryHelper().update(new String[]{"title=?","content=?","`order`=?","catalog=?","comment=?","oneself=?"}, 
				new String[]{"id=?"}, 
				new Object[]{blog.getTitle(),blog.getContent(),blog.getOrder(),blog.getCatalog(),blog.getComment(),blog.getOneself(),blog.getId()},BlogVo.class);
	}
	
	/**
	 * 更新推荐时间
	 * @param id
	 * @param time
	 * @return
	 */
	public int updateRecommendtime(int id,long time)
	{
		updateCache(id,new String[]{"recommend_time"},new Object[]{time});
		return (int) getQueryHelper().update(new String[]{"recommend_time=?"}, new String[]{"id=?"}, new Object[]{time,id},BlogVo.class);
	}
	
	/**
	 * 创建
	 * @param blog
	 * @return
	 */
	public Blog create(Blog blog)
	{
		createIndex(new String[]{"id","title","content","type","oneself","create_time"}, new Object[]{blog.getId(),blog.getTitle(),blog.getContent(),blog.getType(),blog.getOneself(),blog.getCreate_time()});

		getQueryHelper().insert(new String[]{"id","author_id","title","content","comment_id","type","catalog","`order`","views_count","comment_count","oneself","comment","ip","favorite_count","recommend_time","create_time"}, 
				new Object[]{blog.getId(),blog.getAuthor(),blog.getTitle(),blog.getContent(),blog.getComment_id(),blog.getType(),blog.getCatalog(),
				blog.getOrder(),blog.getViews_count(),blog.getComment_count(),blog.getOneself(),blog.getComment(),blog.getIp(),blog.getFavorite_count(),blog.getRecommend_time(),blog.getCreate_time()},BlogVo.class);
		
		if(isNeedCache(blog.getAuthor()))
			getCacheHelper().del(getByTypeAndAuthorAndCatalogCache(blog.getType(),blog.getAuthor(),blog.getCatalog()));
		
		return blog;
	}
	
	public String getIdKey(Object id)
	{
		return getKey(getCacheName(),id);
	}
	
	private void updateCache(int id,String fields[],Object values[])
	{
		updateCache(getCacheName(),id,fields,values,getTimeout());
	}
	
	private void cacheData(BlogVo blogVo)
	{
		commentDAO.needCache(blogVo.getComment_id());
	}
	
	private boolean isNeedCache(int user)
	{
		return userDAO.isInCache(user) || userDAO.isOnline(user);
	}
	
	private String getCacheName()
	{
		return Constants.CACHE_BLOG;
	}

	private int getTimeout()
	{
		return Constants.TIMEOUT_BLOG;
	}
	
	private void updateIndexById(int id,String fields[],Object values[])
	{
		indexHandle(new IndexHandleBean(Constants.INDEX_PATH_BLOG,"id:"+id,fields, values));
	}
	
	private void createIndex(String fields[],Object values[])
	{
		indexHandle(new IndexHandleBean(Constants.INDEX_PATH_BLOG,fields, values));
	}
	
	private void indexHandle(IndexHandleBean indexHandleBean)
	{
		getMessageQueueHelper().send(Constants.QUEUE_INDEX_HANDLE,indexHandleBean);
	}
	
}